This session will continue to explore the uses of
ggplot2 and other packages that can improve your data
visualization and statistical analyses.
Let’s load the required packages
packages_to_install <- c("tidyverse","gapminder","ggpubr","ggridges","ggreveal")
for (package in packages_to_install) {
if (!(package %in% rownames(installed.packages()))) {
install.packages(package, dependencies = TRUE)
print(paste("Installed package:", package))
} else {
print(paste(package, "is already installed"))
}
}
[1] "tidyverse is already installed"
[1] "gapminder is already installed"
[1] "ggpubr is already installed"
[1] "ggridges is already installed"
[1] "ggreveal is already installed"
library(tidyverse)
library(gapminder)
library(ggpubr)
library(ggridges)
library(ggreveal)
Now that we understand the building blocks that go into creating a
plot with ggplot we can make our lives easier by setting a
universal theme at the top of our script that will help keep our figures
looking consistent.
theme_set(theme_bw()) # sets black and white theme for all plots generated in the document
theme_replace( # sets individual plot elements without overwriting theme
axis.title.x = element_text(size = 14),
axis.title.y = element_text(size = 14,
angle = 90,),
axis.text.x = element_text(size = 12),
axis.text.y = element_text(size = 12),
legend.title = element_text(size = 14),
legend.text = element_text(size = 12)
)
You can still manually set all of these plot elements in the plot
code itself, but this means that every plot we make shouldn’t require
quite so much coding to create a consistent visual style.
Create histograms and violin plots to examine distribution
Let’s look at the basic scatter plot we created before using the Iris
dataset.
ggplot(data = iris,
aes(x = Petal.Length,
y = Petal.Width)) +
geom_point(aes(color = Species))

Histograms offer several benefits in data analysis and visualization.
They’re an important part of exploratory analysis of your data.
Visualizing Distribution and Symmetry: Histograms provide a
visual representation of the distribution of a dataset. They allow you
to see the shape, center, and spread of the data. By examining the
histogram, you can quickly identify if the data are skewed, symmetric,
or bimodal, which can provide insights into the underlying patterns and
characteristics of the data. Understanding the skewness of the data can
guide decisions regarding appropriate statistical methods and data
transformations.
Identifying Outliers: Histograms can help identify potential
outliers or unusual observations in the data. Outliers appear as bars
that are significantly separated from the main distribution or as
isolated bars with very low frequencies. Detecting outliers is important
for understanding data quality and assessing the impact they may have on
statistical analyses.
Data Exploration and Comparison: Histograms allow for the
exploration and comparison of multiple variables or subgroups within a
dataset. By creating separate histograms for different groups or
categories, you can visually compare their distributions and understand
the similarities or differences between them.
Let’s plot a basic histogram of Petal.Length, colored by
Species
ggplot(data = iris,
aes(x = Petal.Length)) +
geom_histogram(aes(fill = Species))

We’ll add black outlines to the bars to make them easier to see

Split the histogram into facets per species.
iris_histo +
facet_grid(cols = vars(Species)) # with facet_grid we can choose whether to wrap in rows or columns

iris_histo +
facet_grid(rows = vars(Species)) # this wraps the data in rows

Plot the Sepal length per species as a violin plot
ggplot(data = iris,
aes(x = Species,
y = Sepal.Length,
fill = Species)) +
geom_violin()

Let’s add quantile information to our plots
ggplot(data = iris,
aes(x = Species,
y = Sepal.Length,
fill = Species)) +
geom_violin(draw_quantiles = c( # draws quantile lines
0.25,0.5,0.75
))

Let’s tidy the plot up a little bit!
ggplot(data = iris,
aes(x = Species,
y = Sepal.Length,
fill = Species)) +
geom_violin(alpha = 0.5,
draw_quantiles = c(0.25,0.5,0.75)) +
labs(y = "Sepal length (mm)")

Checking the normality of data
We can check normality of data using the Shapiro-Wilk Test. The
shapiro.test function takes a single numeric vector as its
argument and returns the result of Shapiro-Wilk test. The test evaluates
the null hypothesis that the population from which the data are drawn is
normally distributed. A P-value less than the significance level
(usually 0.05) suggests that the data are not normally
distributed.
lapply(iris[,1:4], # use lapply to apply the shapiro.test function to multiple columns
function(x) shapiro.test(x)) -> normality_tests
# Print the results of normality tests. The results of the normality tests are stored in the normalityTests list, with variable names assigned using names().
names(normality_tests) <- colnames(iris[, 1:4])
print(normality_tests)
$Sepal.Length
Shapiro-Wilk normality test
data: x
W = 0.97609, p-value = 0.01018
$Sepal.Width
Shapiro-Wilk normality test
data: x
W = 0.98492, p-value = 0.1012
$Petal.Length
Shapiro-Wilk normality test
data: x
W = 0.87627, p-value = 7.412e-10
$Petal.Width
Shapiro-Wilk normality test
data: x
W = 0.90183, p-value = 1.68e-08
According to these test results, our only normally distributed data
are the Sepal.Width measurements. Let’s check this by plotting
histograms for each variable using hist(). The
par(mfrow = c(2,2)) command sets up a 2 x 2 grid layout for
the histograms, allowing them to be displayed in separate panels.
par(mfrow = c(2, 2))
hist(iris$Sepal.Length, main = "Sepal Length", xlab = "Length")
hist(iris$Sepal.Width, main = "Sepal Width", xlab = "Width")
hist(iris$Petal.Length, main = "Petal Length", xlab = "Length")
hist(iris$Petal.Width, main = "Petal Width", xlab = "Width")

A slightly more attractive (but possibly less informative) way to
visualize this data is using density ridges from
ggridges.
To use this, we’ll need to adjust the layout of the iris
dataframe to a long format.
iris %>%
pivot_longer(cols = 1:4, # select measurement columns
names_to = "measurement",# set column name for variables
values_to = "value") %>% # set column name for measurements
ggplot(aes(x = value,
y = measurement,
fill = Species)) +
geom_density_ridges(
alpha = 0.5, # set opacity of ridges
scale = 0.98 # set height scaling of ridges
) +
scale_x_continuous(name = "Measurement (cm)") +
scale_y_discrete(name = NULL,
labels = c("Petal.Length" = "Petal Length",
"Petal.Width" = "Petal Width",
"Sepal.Length" = "Sepal Length",
"Sepal.Width" = "Sepal Width"))

Other density plots
Another way to visualize your data is using density plots, also known
as kernel density plots. There are several benefits to visualizing your
data through density plots:
Visualize distribution: Density plots provide a smooth,
continuous estimate of the underlying probability density function (PDF)
of a variable. Unlike histograms, which use discrete bins, density plots
show the shape of the distribution without the binning bias. They
provide a more detailed and nuanced representation of the data
distribution.
Non-Parametric Estimation: Density plots do not assume a specific
distributional form for the data. They are non-parametric and estimate
the density function based on the observed data points. This flexibility
makes them suitable for exploring and visualizing data that may not
adhere to a specific distribution.
Comparison and Overlapping Distributions: Density plots make it
easy to compare multiple distributions or visualize the overlap of
different groups. By using different colors or line types, you can
distinguish and compare distributions visually. This is particularly
useful for identifying differences, similarities, or shifts in
distributions across different groups or variables.
Communicating Findings: Density plots provide an effective way to
communicate distributional characteristics and patterns to others. They
offer a visually appealing and intuitive representation of data
distributions, making it easier for others to grasp the underlying shape
and variability.
ggplot(data = gapminder,
aes(x = gdpPercap,
y = lifeExp)) +
geom_bin2d(
bins = c(50,50) # vectors for number of horizontal and vertical bins
) +
scale_fill_viridis_c(
name = "Number of countries"
) + # uses a viridis scale which is easier to visualize
labs(x = "GDP per capita (USD)", # set x axis title
y = "Life expectancy (years)") + # set y axis title
theme(
legend.position = "top", # set legend position
legend.key.width = unit(2, "cm"), # set legend key width
legend.title.position = "top" # move legend title
)

Adding statistics to your plots
ggplot has arguments for completing simple statistical
tests through the extension ggpubr.
ggpubr offers additional functions and utilities for
data visualization and statistical analysis, specifically designed to
work seamlessly with ggplot2. Let’s add statistics to some
box plots. Here we will compare mean life expectancy between continents
using the gapminder dataset.
Reference: http://www.sthda.com/english/articles/24-ggpubr-publication-ready-plots/76-add-p-values-and-significance-levels-to-ggplots/#google_vignette
First we need to check the normality of our data
shapiro.test(gapminder$lifeExp)
Shapiro-Wilk normality test
data: gapminder$lifeExp
W = 0.95248, p-value < 2.2e-16
Our P value is much lower than the 0.05 cutoff value which
means our data are not normally distributed, so we need to take that
into account when choosing a statistical test.
What test would you use to compare means across continents if the
data were normally distributed?

Our Kruskal-Wallis test tells us that there is a significant
difference between groups, but not between which groups. If our data
were normal and we performed an ANOVA we would perform a post-hoc test.
Instead, we can use a pairwise Wilcoxon rank sum test.
pairwise.wilcox.test(gapminder$lifeExp,
gapminder$continent,
p.adjust.method = "BH")
Pairwise comparisons using Wilcoxon rank sum test with continuity correction
data: gapminder$lifeExp and gapminder$continent
Africa Americas Asia Europe
Americas < 2e-16 - - -
Asia < 2e-16 3.5e-07 - -
Europe < 2e-16 < 2e-16 < 2e-16 -
Oceania 9.3e-16 5.7e-08 6.4e-10 0.047
P value adjustment method: BH
Now we can see that all continents are significantly different from
one another!
Preparing plots for presentation
Sometimes we have very busy plots that would benefit from being
revealed in sequence as part of storytelling in a presentation. For
that, we can use ggreveal, which allows us to reveal parts
of a plot sequentially and save each intermediate step for use. Let’s
look at the changes in life expectancy over several decades.

Now that we’ve created our life expectancy plot we can choose how to
reveal the layers.
plot_list
[[1]]
[[2]]
[[3]]
[[4]]
[[5]]
[[6]]
[[7]]







You can click through the different plots that ggreveal
has made. Now let’s save all these plots individually.
reveal_save(
plot_list, # list of plots
"life_exp.tiff"
)
Activities
Green 1
These activities will use the gapminder dataset which is
a subset of country-by-country GDP from gapminder.org from 2010 and
another of R’s built-in datasets, diamonds, which contains
data on diamond size, clarity, and color.
Perform normality tests on the gapminder dataset. Look specifically
at the normality of the data from Europe.
Green 2
Create a histogram using the diamonds data set. Facet so
that they are aligned vertically.
Blue 1
Create a 2D bin (density) plot showing GDP Per Capita and Life
Expectancy using geom_bind2d(). Then, come up with a way to
show all five continents separately
Blue 2
Now add density lines to your plot using
geom_density2d().
Black 1
Using the gapminder data, use one or more techniques to show the
distribution of Life Expectancy over time for each continent.
Hint: Explore the geoms we’ve used to day, or try a new
one! You can look up all available geoms here: https://ggplot2.tidyverse.org/reference/index.html
Black 2
Visualize specific comparisons between continents and label with
brackets and P values or significance markers.
Hint: use the reference above to work out how to do
this
LS0tCnRpdGxlOiAiRGF0YSB2aXN1YWxpemF0aW9uIGFuZCBzdGF0aXN0aWNzIgphdXRob3I6ICJZYXNtaW4gSGlsbGlhbSwgUGhEIgpkYXRlOiAiMjAyNS0wNy0wNSIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBzZXNzaW9uIHdpbGwgY29udGludWUgdG8gZXhwbG9yZSB0aGUgdXNlcyBvZiBgZ2dwbG90MmAgYW5kIG90aGVyIHBhY2thZ2VzIHRoYXQgY2FuIGltcHJvdmUgeW91ciBkYXRhIHZpc3VhbGl6YXRpb24gYW5kIHN0YXRpc3RpY2FsIGFuYWx5c2VzLgoKTGV0J3MgbG9hZCB0aGUgcmVxdWlyZWQgcGFja2FnZXMKCmBgYHtyfQpwYWNrYWdlc190b19pbnN0YWxsIDwtIGMoInRpZHl2ZXJzZSIsImdhcG1pbmRlciIsImdncHViciIsImdncmlkZ2VzIiwiZ2dyZXZlYWwiKQoKZm9yIChwYWNrYWdlIGluIHBhY2thZ2VzX3RvX2luc3RhbGwpIHsKICBpZiAoIShwYWNrYWdlICVpbiUgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSkgewogICAgaW5zdGFsbC5wYWNrYWdlcyhwYWNrYWdlLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICAgcHJpbnQocGFzdGUoIkluc3RhbGxlZCBwYWNrYWdlOiIsIHBhY2thZ2UpKQogIH0gZWxzZSB7CiAgICBwcmludChwYXN0ZShwYWNrYWdlLCAiaXMgYWxyZWFkeSBpbnN0YWxsZWQiKSkKICB9Cn0KCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdhcG1pbmRlcikKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZ2dyZXZlYWwpCmBgYAoKTm93IHRoYXQgd2UgdW5kZXJzdGFuZCB0aGUgYnVpbGRpbmcgYmxvY2tzIHRoYXQgZ28gaW50byBjcmVhdGluZyBhIHBsb3Qgd2l0aCBgZ2dwbG90YCB3ZSBjYW4gbWFrZSBvdXIgbGl2ZXMgZWFzaWVyIGJ5IHNldHRpbmcgYSB1bml2ZXJzYWwgdGhlbWUgYXQgdGhlIHRvcCBvZiBvdXIgc2NyaXB0IHRoYXQgd2lsbCBoZWxwIGtlZXAgb3VyIGZpZ3VyZXMgbG9va2luZyBjb25zaXN0ZW50LgoKYGBge3J9CnRoZW1lX3NldCh0aGVtZV9idygpKSAjIHNldHMgYmxhY2sgYW5kIHdoaXRlIHRoZW1lIGZvciBhbGwgcGxvdHMgZ2VuZXJhdGVkIGluIHRoZSBkb2N1bWVudAoKdGhlbWVfcmVwbGFjZSggIyBzZXRzIGluZGl2aWR1YWwgcGxvdCBlbGVtZW50cyB3aXRob3V0IG92ZXJ3cml0aW5nIHRoZW1lCiAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYW5nbGUgPSA5MCwpLAogIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwKICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KSwKICBsZWdlbmQudGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpCikKYGBgCgpZb3UgY2FuIHN0aWxsIG1hbnVhbGx5IHNldCBhbGwgb2YgdGhlc2UgcGxvdCBlbGVtZW50cyBpbiB0aGUgcGxvdCBjb2RlIGl0c2VsZiwgYnV0IHRoaXMgbWVhbnMgdGhhdCBldmVyeSBwbG90IHdlIG1ha2Ugc2hvdWxkbid0IHJlcXVpcmUgcXVpdGUgc28gbXVjaCBjb2RpbmcgdG8gY3JlYXRlIGEgY29uc2lzdGVudCB2aXN1YWwgc3R5bGUuCgojIyMjIENyZWF0ZSBoaXN0b2dyYW1zIGFuZCB2aW9saW4gcGxvdHMgdG8gZXhhbWluZSBkaXN0cmlidXRpb24KCkxldCdzIGxvb2sgYXQgdGhlIGJhc2ljIHNjYXR0ZXIgcGxvdCB3ZSBjcmVhdGVkIGJlZm9yZSB1c2luZyB0aGUgSXJpcyBkYXRhc2V0LgoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIGFlcyh4ID0gUGV0YWwuTGVuZ3RoLCAKICAgICAgICAgICB5ID0gUGV0YWwuV2lkdGgpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3IgPSBTcGVjaWVzKSkKYGBgCgpIaXN0b2dyYW1zIG9mZmVyIHNldmVyYWwgYmVuZWZpdHMgaW4gZGF0YSBhbmFseXNpcyBhbmQgdmlzdWFsaXphdGlvbi4gVGhleSdyZSBhbiBpbXBvcnRhbnQgcGFydCBvZiBleHBsb3JhdG9yeSBhbmFseXNpcyBvZiB5b3VyIGRhdGEuCgotICAgVmlzdWFsaXppbmcgRGlzdHJpYnV0aW9uIGFuZCBTeW1tZXRyeTogSGlzdG9ncmFtcyBwcm92aWRlIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gb2YgYSBkYXRhc2V0LiBUaGV5IGFsbG93IHlvdSB0byBzZWUgdGhlIHNoYXBlLCBjZW50ZXIsIGFuZCBzcHJlYWQgb2YgdGhlIGRhdGEuIEJ5IGV4YW1pbmluZyB0aGUgaGlzdG9ncmFtLCB5b3UgY2FuIHF1aWNrbHkgaWRlbnRpZnkgaWYgdGhlIGRhdGEgYXJlIHNrZXdlZCwgc3ltbWV0cmljLCBvciBiaW1vZGFsLCB3aGljaCBjYW4gcHJvdmlkZSBpbnNpZ2h0cyBpbnRvIHRoZSB1bmRlcmx5aW5nIHBhdHRlcm5zIGFuZCBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIGRhdGEuIFVuZGVyc3RhbmRpbmcgdGhlIHNrZXduZXNzIG9mIHRoZSBkYXRhIGNhbiBndWlkZSBkZWNpc2lvbnMgcmVnYXJkaW5nIGFwcHJvcHJpYXRlIHN0YXRpc3RpY2FsIG1ldGhvZHMgYW5kIGRhdGEgdHJhbnNmb3JtYXRpb25zLgoKLSAgIElkZW50aWZ5aW5nIE91dGxpZXJzOiBIaXN0b2dyYW1zIGNhbiBoZWxwIGlkZW50aWZ5IHBvdGVudGlhbCBvdXRsaWVycyBvciB1bnVzdWFsIG9ic2VydmF0aW9ucyBpbiB0aGUgZGF0YS4gT3V0bGllcnMgYXBwZWFyIGFzIGJhcnMgdGhhdCBhcmUgc2lnbmlmaWNhbnRseSBzZXBhcmF0ZWQgZnJvbSB0aGUgbWFpbiBkaXN0cmlidXRpb24gb3IgYXMgaXNvbGF0ZWQgYmFycyB3aXRoIHZlcnkgbG93IGZyZXF1ZW5jaWVzLiBEZXRlY3Rpbmcgb3V0bGllcnMgaXMgaW1wb3J0YW50IGZvciB1bmRlcnN0YW5kaW5nIGRhdGEgcXVhbGl0eSBhbmQgYXNzZXNzaW5nIHRoZSBpbXBhY3QgdGhleSBtYXkgaGF2ZSBvbiBzdGF0aXN0aWNhbCBhbmFseXNlcy4KCi0gICBEYXRhIEV4cGxvcmF0aW9uIGFuZCBDb21wYXJpc29uOiBIaXN0b2dyYW1zIGFsbG93IGZvciB0aGUgZXhwbG9yYXRpb24gYW5kIGNvbXBhcmlzb24gb2YgbXVsdGlwbGUgdmFyaWFibGVzIG9yIHN1Ymdyb3VwcyB3aXRoaW4gYSBkYXRhc2V0LiBCeSBjcmVhdGluZyBzZXBhcmF0ZSBoaXN0b2dyYW1zIGZvciBkaWZmZXJlbnQgZ3JvdXBzIG9yIGNhdGVnb3JpZXMsIHlvdSBjYW4gdmlzdWFsbHkgY29tcGFyZSB0aGVpciBkaXN0cmlidXRpb25zIGFuZCB1bmRlcnN0YW5kIHRoZSBzaW1pbGFyaXRpZXMgb3IgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGVtLgoKTGV0J3MgcGxvdCBhIGJhc2ljIGhpc3RvZ3JhbSBvZiBgUGV0YWwuTGVuZ3RoYCwgY29sb3JlZCBieSBgU3BlY2llc2AKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsCiAgICAgICBhZXMoeCA9IFBldGFsLkxlbmd0aCkpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoZmlsbCA9IFNwZWNpZXMpKQpgYGAKCldlJ2xsIGFkZCBibGFjayBvdXRsaW5lcyB0byB0aGUgYmFycyB0byBtYWtlIHRoZW0gZWFzaWVyIHRvIHNlZQoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIGFlcyh4ID0gUGV0YWwuTGVuZ3RoKSkgKwogIGdlb21faGlzdG9ncmFtKAogICAgYWVzKGZpbGwgPSBTcGVjaWVzKSwKICAgIGNvbG9yID0gImJsYWNrIiAjIGFkZHMgYmxhY2sgb3V0bGluZSB0byBhbGwgYmFycwogICkgLT4gaXJpc19oaXN0bwpgYGAKClNwbGl0IHRoZSBoaXN0b2dyYW0gaW50byBmYWNldHMgcGVyIHNwZWNpZXMuCgpgYGB7cn0KaXJpc19oaXN0byArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhTcGVjaWVzKSkgIyB3aXRoIGZhY2V0X2dyaWQgd2UgY2FuIGNob29zZSB3aGV0aGVyIHRvIHdyYXAgaW4gcm93cyBvciBjb2x1bW5zCgppcmlzX2hpc3RvICsKICBmYWNldF9ncmlkKHJvd3MgPSB2YXJzKFNwZWNpZXMpKSAjIHRoaXMgd3JhcHMgdGhlIGRhdGEgaW4gcm93cwpgYGAKClBsb3QgdGhlIFNlcGFsIGxlbmd0aCBwZXIgc3BlY2llcyBhcyBhIHZpb2xpbiBwbG90CgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBpcmlzLAogICAgICAgYWVzKHggPSBTcGVjaWVzLAogICAgICAgICAgIHkgPSBTZXBhbC5MZW5ndGgsCiAgICAgICAgICAgZmlsbCA9IFNwZWNpZXMpKSArCiAgZ2VvbV92aW9saW4oKQpgYGAKCkxldCdzIGFkZCBxdWFudGlsZSBpbmZvcm1hdGlvbiB0byBvdXIgcGxvdHMKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGlyaXMsCiAgICAgICBhZXMoeCA9IFNwZWNpZXMsCiAgICAgICAgICAgeSA9IFNlcGFsLkxlbmd0aCwKICAgICAgICAgICBmaWxsID0gU3BlY2llcykpICsKICBnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoICMgZHJhd3MgcXVhbnRpbGUgbGluZXMKICAgIDAuMjUsMC41LDAuNzUKICApKQpgYGAKCkxldCdzIHRpZHkgdGhlIHBsb3QgdXAgYSBsaXR0bGUgYml0IQoKYGBge3J9CmdncGxvdChkYXRhID0gaXJpcywKICAgICAgIGFlcyh4ID0gU3BlY2llcywKICAgICAgICAgICB5ID0gU2VwYWwuTGVuZ3RoLAogICAgICAgICAgIGZpbGwgPSBTcGVjaWVzKSkgKwogIGdlb21fdmlvbGluKGFscGhhID0gMC41LAogICAgICAgICAgICAgIGRyYXdfcXVhbnRpbGVzID0gYygwLjI1LDAuNSwwLjc1KSkgKwogIGxhYnMoeSA9ICJTZXBhbCBsZW5ndGggKG1tKSIpCmBgYAoKIyMjIyBDaGVja2luZyB0aGUgbm9ybWFsaXR5IG9mIGRhdGEKCldlIGNhbiBjaGVjayBub3JtYWxpdHkgb2YgZGF0YSB1c2luZyB0aGUgU2hhcGlyby1XaWxrIFRlc3QuIFRoZSBgc2hhcGlyby50ZXN0YCBmdW5jdGlvbiB0YWtlcyBhIHNpbmdsZSBudW1lcmljIHZlY3RvciBhcyBpdHMgYXJndW1lbnQgYW5kIHJldHVybnMgdGhlIHJlc3VsdCBvZiBTaGFwaXJvLVdpbGsgdGVzdC4gVGhlIHRlc3QgZXZhbHVhdGVzIHRoZSBudWxsIGh5cG90aGVzaXMgdGhhdCB0aGUgcG9wdWxhdGlvbiBmcm9tIHdoaWNoIHRoZSBkYXRhIGFyZSBkcmF3biBpcyBub3JtYWxseSBkaXN0cmlidXRlZC4gQSBQLXZhbHVlIGxlc3MgdGhhbiB0aGUgc2lnbmlmaWNhbmNlIGxldmVsICh1c3VhbGx5IDAuMDUpIHN1Z2dlc3RzIHRoYXQgdGhlIGRhdGEgYXJlICoqbm90Kiogbm9ybWFsbHkgZGlzdHJpYnV0ZWQuCgpgYGB7cn0KbGFwcGx5KGlyaXNbLDE6NF0sICMgdXNlIGxhcHBseSB0byBhcHBseSB0aGUgc2hhcGlyby50ZXN0IGZ1bmN0aW9uIHRvIG11bHRpcGxlIGNvbHVtbnMKICAgICAgIGZ1bmN0aW9uKHgpIHNoYXBpcm8udGVzdCh4KSkgLT4gbm9ybWFsaXR5X3Rlc3RzCgojIFByaW50IHRoZSByZXN1bHRzIG9mIG5vcm1hbGl0eSB0ZXN0cy4gVGhlIHJlc3VsdHMgb2YgdGhlIG5vcm1hbGl0eSB0ZXN0cyBhcmUgc3RvcmVkIGluIHRoZSBub3JtYWxpdHlUZXN0cyBsaXN0LCB3aXRoIHZhcmlhYmxlIG5hbWVzIGFzc2lnbmVkIHVzaW5nIG5hbWVzKCkuCm5hbWVzKG5vcm1hbGl0eV90ZXN0cykgPC0gY29sbmFtZXMoaXJpc1ssIDE6NF0pCnByaW50KG5vcm1hbGl0eV90ZXN0cykKYGBgCgpBY2NvcmRpbmcgdG8gdGhlc2UgdGVzdCByZXN1bHRzLCBvdXIgb25seSBub3JtYWxseSBkaXN0cmlidXRlZCBkYXRhIGFyZSB0aGUgU2VwYWwuV2lkdGggbWVhc3VyZW1lbnRzLiBMZXQncyBjaGVjayB0aGlzIGJ5IHBsb3R0aW5nIGhpc3RvZ3JhbXMgZm9yIGVhY2ggdmFyaWFibGUgdXNpbmcgYGhpc3QoKWAuIFRoZSBgcGFyKG1mcm93ID0gYygyLDIpKWAgY29tbWFuZCBzZXRzIHVwIGEgMiB4IDIgZ3JpZCBsYXlvdXQgZm9yIHRoZSBoaXN0b2dyYW1zLCBhbGxvd2luZyB0aGVtIHRvIGJlIGRpc3BsYXllZCBpbiBzZXBhcmF0ZSBwYW5lbHMuCgpgYGB7cn0KcGFyKG1mcm93ID0gYygyLCAyKSkKCmhpc3QoaXJpcyRTZXBhbC5MZW5ndGgsIG1haW4gPSAiU2VwYWwgTGVuZ3RoIiwgeGxhYiA9ICJMZW5ndGgiKQoKaGlzdChpcmlzJFNlcGFsLldpZHRoLCBtYWluID0gIlNlcGFsIFdpZHRoIiwgeGxhYiA9ICJXaWR0aCIpCgpoaXN0KGlyaXMkUGV0YWwuTGVuZ3RoLCBtYWluID0gIlBldGFsIExlbmd0aCIsIHhsYWIgPSAiTGVuZ3RoIikKCmhpc3QoaXJpcyRQZXRhbC5XaWR0aCwgbWFpbiA9ICJQZXRhbCBXaWR0aCIsIHhsYWIgPSAiV2lkdGgiKQpgYGAKCkEgc2xpZ2h0bHkgbW9yZSBhdHRyYWN0aXZlIChidXQgcG9zc2libHkgbGVzcyBpbmZvcm1hdGl2ZSkgd2F5IHRvIHZpc3VhbGl6ZSB0aGlzIGRhdGEgaXMgdXNpbmcgZGVuc2l0eSByaWRnZXMgZnJvbSBgZ2dyaWRnZXNgLgoKVG8gdXNlIHRoaXMsIHdlJ2xsIG5lZWQgdG8gYWRqdXN0IHRoZSBsYXlvdXQgb2YgdGhlIGBpcmlzYCBkYXRhZnJhbWUgdG8gYSBsb25nIGZvcm1hdC4KCmBgYHtyfQppcmlzICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gMTo0LCAjIHNlbGVjdCBtZWFzdXJlbWVudCBjb2x1bW5zCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIm1lYXN1cmVtZW50IiwjIHNldCBjb2x1bW4gbmFtZSBmb3IgdmFyaWFibGVzCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJ2YWx1ZSIpICU+JSAjIHNldCBjb2x1bW4gbmFtZSBmb3IgbWVhc3VyZW1lbnRzCiAgZ2dwbG90KGFlcyh4ID0gdmFsdWUsCiAgICAgICAgICAgICB5ID0gbWVhc3VyZW1lbnQsCiAgICAgICAgICAgICBmaWxsID0gU3BlY2llcykpICsKICBnZW9tX2RlbnNpdHlfcmlkZ2VzKAogICAgYWxwaGEgPSAwLjUsICMgc2V0IG9wYWNpdHkgb2YgcmlkZ2VzCiAgICBzY2FsZSA9IDAuOTggIyBzZXQgaGVpZ2h0IHNjYWxpbmcgb2YgcmlkZ2VzCiAgKSArCiAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiTWVhc3VyZW1lbnQgKGNtKSIpICsKICBzY2FsZV95X2Rpc2NyZXRlKG5hbWUgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygiUGV0YWwuTGVuZ3RoIiA9ICJQZXRhbCBMZW5ndGgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQZXRhbC5XaWR0aCIgPSAiUGV0YWwgV2lkdGgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTZXBhbC5MZW5ndGgiID0gIlNlcGFsIExlbmd0aCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNlcGFsLldpZHRoIiA9ICJTZXBhbCBXaWR0aCIpKQpgYGAKCiMjIyMgT3RoZXIgZGVuc2l0eSBwbG90cwoKQW5vdGhlciB3YXkgdG8gdmlzdWFsaXplIHlvdXIgZGF0YSBpcyB1c2luZyBkZW5zaXR5IHBsb3RzLCBhbHNvIGtub3duIGFzIGtlcm5lbCBkZW5zaXR5IHBsb3RzLiBUaGVyZSBhcmUgc2V2ZXJhbCBiZW5lZml0cyB0byB2aXN1YWxpemluZyB5b3VyIGRhdGEgdGhyb3VnaCBkZW5zaXR5IHBsb3RzOgoKLSAgIFZpc3VhbGl6ZSBkaXN0cmlidXRpb246IERlbnNpdHkgcGxvdHMgcHJvdmlkZSBhIHNtb290aCwgY29udGludW91cyBlc3RpbWF0ZSBvZiB0aGUgdW5kZXJseWluZyBwcm9iYWJpbGl0eSBkZW5zaXR5IGZ1bmN0aW9uIChQREYpIG9mIGEgdmFyaWFibGUuIFVubGlrZSBoaXN0b2dyYW1zLCB3aGljaCB1c2UgZGlzY3JldGUgYmlucywgZGVuc2l0eSBwbG90cyBzaG93IHRoZSBzaGFwZSBvZiB0aGUgZGlzdHJpYnV0aW9uIHdpdGhvdXQgdGhlIGJpbm5pbmcgYmlhcy4gVGhleSBwcm92aWRlIGEgbW9yZSBkZXRhaWxlZCBhbmQgbnVhbmNlZCByZXByZXNlbnRhdGlvbiBvZiB0aGUgZGF0YSBkaXN0cmlidXRpb24uCgotICAgTm9uLVBhcmFtZXRyaWMgRXN0aW1hdGlvbjogRGVuc2l0eSBwbG90cyBkbyBub3QgYXNzdW1lIGEgc3BlY2lmaWMgZGlzdHJpYnV0aW9uYWwgZm9ybSBmb3IgdGhlIGRhdGEuIFRoZXkgYXJlIG5vbi1wYXJhbWV0cmljIGFuZCBlc3RpbWF0ZSB0aGUgZGVuc2l0eSBmdW5jdGlvbiBiYXNlZCBvbiB0aGUgb2JzZXJ2ZWQgZGF0YSBwb2ludHMuIFRoaXMgZmxleGliaWxpdHkgbWFrZXMgdGhlbSBzdWl0YWJsZSBmb3IgZXhwbG9yaW5nIGFuZCB2aXN1YWxpemluZyBkYXRhIHRoYXQgbWF5IG5vdCBhZGhlcmUgdG8gYSBzcGVjaWZpYyBkaXN0cmlidXRpb24uCgotICAgQ29tcGFyaXNvbiBhbmQgT3ZlcmxhcHBpbmcgRGlzdHJpYnV0aW9uczogRGVuc2l0eSBwbG90cyBtYWtlIGl0IGVhc3kgdG8gY29tcGFyZSBtdWx0aXBsZSBkaXN0cmlidXRpb25zIG9yIHZpc3VhbGl6ZSB0aGUgb3ZlcmxhcCBvZiBkaWZmZXJlbnQgZ3JvdXBzLiBCeSB1c2luZyBkaWZmZXJlbnQgY29sb3JzIG9yIGxpbmUgdHlwZXMsIHlvdSBjYW4gZGlzdGluZ3Vpc2ggYW5kIGNvbXBhcmUgZGlzdHJpYnV0aW9ucyB2aXN1YWxseS4gVGhpcyBpcyBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvciBpZGVudGlmeWluZyBkaWZmZXJlbmNlcywgc2ltaWxhcml0aWVzLCBvciBzaGlmdHMgaW4gZGlzdHJpYnV0aW9ucyBhY3Jvc3MgZGlmZmVyZW50IGdyb3VwcyBvciB2YXJpYWJsZXMuCgotICAgQ29tbXVuaWNhdGluZyBGaW5kaW5nczogRGVuc2l0eSBwbG90cyBwcm92aWRlIGFuIGVmZmVjdGl2ZSB3YXkgdG8gY29tbXVuaWNhdGUgZGlzdHJpYnV0aW9uYWwgY2hhcmFjdGVyaXN0aWNzIGFuZCBwYXR0ZXJucyB0byBvdGhlcnMuIFRoZXkgb2ZmZXIgYSB2aXN1YWxseSBhcHBlYWxpbmcgYW5kIGludHVpdGl2ZSByZXByZXNlbnRhdGlvbiBvZiBkYXRhIGRpc3RyaWJ1dGlvbnMsIG1ha2luZyBpdCBlYXNpZXIgZm9yIG90aGVycyB0byBncmFzcCB0aGUgdW5kZXJseWluZyBzaGFwZSBhbmQgdmFyaWFiaWxpdHkuCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBnYXBtaW5kZXIsIAogICAgICAgYWVzKHggPSBnZHBQZXJjYXAsIAogICAgICAgICAgIHkgPSBsaWZlRXhwKSkgKwogIGdlb21fYmluMmQoCiAgICBiaW5zID0gYyg1MCw1MCkgIyB2ZWN0b3JzIGZvciBudW1iZXIgb2YgaG9yaXpvbnRhbCBhbmQgdmVydGljYWwgYmlucwogICkgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19jKAogICAgbmFtZSA9ICJOdW1iZXIgb2YgY291bnRyaWVzIgogICkgKyAjIHVzZXMgYSB2aXJpZGlzIHNjYWxlIHdoaWNoIGlzIGVhc2llciB0byB2aXN1YWxpemUKICBsYWJzKHggPSAiR0RQIHBlciBjYXBpdGEgKFVTRCkiLCAjIHNldCB4IGF4aXMgdGl0bGUKICAgICAgIHkgPSAiTGlmZSBleHBlY3RhbmN5ICh5ZWFycykiKSArICMgc2V0IHkgYXhpcyB0aXRsZQogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gInRvcCIsICMgc2V0IGxlZ2VuZCBwb3NpdGlvbgogICAgbGVnZW5kLmtleS53aWR0aCA9IHVuaXQoMiwgImNtIiksICMgc2V0IGxlZ2VuZCBrZXkgd2lkdGgKICAgIGxlZ2VuZC50aXRsZS5wb3NpdGlvbiA9ICJ0b3AiICMgbW92ZSBsZWdlbmQgdGl0bGUgCiAgKQpgYGAKCiMjIyMgQWRkaW5nIHN0YXRpc3RpY3MgdG8geW91ciBwbG90cwoKYGdncGxvdGAgaGFzIGFyZ3VtZW50cyBmb3IgY29tcGxldGluZyBzaW1wbGUgc3RhdGlzdGljYWwgdGVzdHMgdGhyb3VnaCB0aGUgZXh0ZW5zaW9uIGBnZ3B1YnJgLgoKYGdncHVicmAgb2ZmZXJzIGFkZGl0aW9uYWwgZnVuY3Rpb25zIGFuZCB1dGlsaXRpZXMgZm9yIGRhdGEgdmlzdWFsaXphdGlvbiBhbmQgc3RhdGlzdGljYWwgYW5hbHlzaXMsIHNwZWNpZmljYWxseSBkZXNpZ25lZCB0byB3b3JrIHNlYW1sZXNzbHkgd2l0aCBgZ2dwbG90MmAuIExldCdzIGFkZCBzdGF0aXN0aWNzIHRvIHNvbWUgYm94IHBsb3RzLiBIZXJlIHdlIHdpbGwgY29tcGFyZSBtZWFuIGxpZmUgZXhwZWN0YW5jeSBiZXR3ZWVuIGNvbnRpbmVudHMgdXNpbmcgdGhlIGBnYXBtaW5kZXJgIGRhdGFzZXQuCgpSZWZlcmVuY2U6IDxodHRwOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL2FydGljbGVzLzI0LWdncHVici1wdWJsaWNhdGlvbi1yZWFkeS1wbG90cy83Ni1hZGQtcC12YWx1ZXMtYW5kLXNpZ25pZmljYW5jZS1sZXZlbHMtdG8tZ2dwbG90cy8jZ29vZ2xlX3ZpZ25ldHRlPgoKRmlyc3Qgd2UgbmVlZCB0byBjaGVjayB0aGUgbm9ybWFsaXR5IG9mIG91ciBkYXRhCgpgYGB7cn0Kc2hhcGlyby50ZXN0KGdhcG1pbmRlciRsaWZlRXhwKQpgYGAKCk91ciBQIHZhbHVlIGlzICptdWNoKiBsb3dlciB0aGFuIHRoZSAwLjA1IGN1dG9mZiB2YWx1ZSB3aGljaCBtZWFucyBvdXIgZGF0YSBhcmUgbm90IG5vcm1hbGx5IGRpc3RyaWJ1dGVkLCBzbyB3ZSBuZWVkIHRvIHRha2UgdGhhdCBpbnRvIGFjY291bnQgd2hlbiBjaG9vc2luZyBhIHN0YXRpc3RpY2FsIHRlc3QuCgpXaGF0IHRlc3Qgd291bGQgeW91IHVzZSB0byBjb21wYXJlIG1lYW5zIGFjcm9zcyBjb250aW5lbnRzIGlmIHRoZSBkYXRhICp3ZXJlKiBub3JtYWxseSBkaXN0cmlidXRlZD8KCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGdhcG1pbmRlciwKICAgICAgIGFlcyh4ID0gY29udGluZW50LAogICAgICAgICAgIHkgPSBsaWZlRXhwLAogICAgICAgICAgIGNvbG9yID0gY29udGluZW50KSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVycyA9IEZBTFNFKSArCiAgZ2VvbV9qaXR0ZXIod2lkdGggPSAwLjIsCiAgICAgICAgICAgICAgYWxwaGEgPSAwLjMpICsKICBzdGF0X2NvbXBhcmVfbWVhbnMoCiAgICBtZXRob2QgPSAia3J1c2thbC50ZXN0IiwKICAgIGxhYmVsLnkgPSA5MCAjIHNldHMgcG9zaXRpb24gb2YgUCB2YWx1ZSBsYWJlbAogICkgKwogIGd1aWRlcyggIyBndWlkZXMgYWxsb3dzIHVzIHRvIGZpbmVseSBtYW5pcHVsYXRlIGxlZ2VuZHMKICAgIGNvbG9yID0gZ3VpZGVfbGVnZW5kKCAjIHdlIHdhbnQgdG8gYWRqdXN0IHRoZSBjb2xvciBsZWdlbmQKICAgICAgb3ZlcnJpZGUuYWVzID0gYWVzKGxhYmVsID0gIiIpICMgcmVtb3ZlIHRoZSBsZWdlbmQgZ3VpZGUgZm9yIHRoZSBsYWJlbAogICAgKQogICkgKwogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKG5hbWUgPSAiQ29udGluZW50IikgKwogIGxhYnMoeCA9IE5VTEwsCiAgICAgICB5ID0gIkxpZmUgZXhwZWN0YW5jeSAoeWVhcnMpIikgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gImluc2lkZSIsICMgbW92ZXMgbGVnZW5kIGluc2lkZSBwbG90IGJvdW5kcwogICAgbGVnZW5kLnBvc2l0aW9uLmluc2lkZSA9IGMoIAogICAgICAwLjksMC4zICMgdmFsdWVzIGZyb20gMC0xIHRoYXQgc2V0IGxlZ2VuZCBob3IuIGFuZCB2ZXIuIHBvc2l0aW9uCiAgICApLCAKICAgIGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KCAjIHNldCBvdXRsaW5lIGNvbG9yIG9mIGxlZ2VuZCBib3gKICAgICAgY29sb3IgPSAiYmxhY2siCiAgICApCiAgKQpgYGAKCk91ciBLcnVza2FsLVdhbGxpcyB0ZXN0IHRlbGxzIHVzIHRoYXQgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBkaWZmZXJlbmNlIGJldHdlZW4gZ3JvdXBzLCBidXQgbm90IGJldHdlZW4gd2hpY2ggZ3JvdXBzLiBJZiBvdXIgZGF0YSB3ZXJlIG5vcm1hbCBhbmQgd2UgcGVyZm9ybWVkIGFuIEFOT1ZBIHdlIHdvdWxkIHBlcmZvcm0gYSBwb3N0LWhvYyB0ZXN0LiBJbnN0ZWFkLCB3ZSBjYW4gdXNlIGEgcGFpcndpc2UgV2lsY294b24gcmFuayBzdW0gdGVzdC4KCmBgYHtyfQpwYWlyd2lzZS53aWxjb3gudGVzdChnYXBtaW5kZXIkbGlmZUV4cCwKICAgICAgICAgICAgICAgICAgICAgZ2FwbWluZGVyJGNvbnRpbmVudCwKICAgICAgICAgICAgICAgICAgICAgcC5hZGp1c3QubWV0aG9kID0gIkJIIikKYGBgCgpOb3cgd2UgY2FuIHNlZSB0aGF0IGFsbCBjb250aW5lbnRzIGFyZSBzaWduaWZpY2FudGx5IGRpZmZlcmVudCBmcm9tIG9uZSBhbm90aGVyIQoKIyMjIyBQcmVwYXJpbmcgcGxvdHMgZm9yIHByZXNlbnRhdGlvbgoKU29tZXRpbWVzIHdlIGhhdmUgdmVyeSBidXN5IHBsb3RzIHRoYXQgd291bGQgYmVuZWZpdCBmcm9tIGJlaW5nIHJldmVhbGVkIGluIHNlcXVlbmNlIGFzIHBhcnQgb2Ygc3Rvcnl0ZWxsaW5nIGluIGEgcHJlc2VudGF0aW9uLiBGb3IgdGhhdCwgd2UgY2FuIHVzZSBgZ2dyZXZlYWxgLCB3aGljaCBhbGxvd3MgdXMgdG8gcmV2ZWFsIHBhcnRzIG9mIGEgcGxvdCBzZXF1ZW50aWFsbHkgYW5kIHNhdmUgZWFjaCBpbnRlcm1lZGlhdGUgc3RlcCBmb3IgdXNlLiBMZXQncyBsb29rIGF0IHRoZSBjaGFuZ2VzIGluIGxpZmUgZXhwZWN0YW5jeSBvdmVyIHNldmVyYWwgZGVjYWRlcy4KCmBgYHtyfQpnYXBtaW5kZXIgJT4lCiAgZmlsdGVyKHllYXIgJWluJSBjKCIxOTUyIiwiMTk2MiIsIjE5NzIiLCIxOTgyIiwiMTk5MiIsIjIwMDIiKSkgJT4lCiAgZmlsdGVyKAogICAgY29udGluZW50ICE9ICJPY2VhbmlhIiAjIHJlbW92aW5nIGR1ZSB0byB0b28gZmV3IGNvdW50cmllcyBmb3IgZGVuc2l0eSBwbG90dGluZwogICkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbGlmZUV4cCwKICAgICAgICAgICAgIHkgPSBjb250aW5lbnQsCiAgICAgICAgICAgICBmaWxsID0gYXMuZmFjdG9yKHllYXIpKSkgKwogIGdlb21fZGVuc2l0eV9yaWRnZXMoYWxwaGEgPSAwLjUsCiAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9IDAuOSkgKwogIGxhYnMoeCA9ICJMaWZlIGV4cGVjdGFuY3kgKHllYXJzKSIsCiAgICAgICB5ID0gIkNvbnRpbmVudCIpICsKICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiWWVhciIpIC0+IGxpZmVfZXhwCmBgYAoKTm93IHRoYXQgd2UndmUgY3JlYXRlZCBvdXIgbGlmZSBleHBlY3RhbmN5IHBsb3Qgd2UgY2FuIGNob29zZSBob3cgdG8gcmV2ZWFsIHRoZSBsYXllcnMuCgpgYGB7cn0KcmV2ZWFsX2FlcyhsaWZlX2V4cCwKICAgICAgICAgICBhZXMgPSAiZmlsbCIpIC0+IHBsb3RfbGlzdAoKcGxvdF9saXN0CmBgYAoKWW91IGNhbiBjbGljayB0aHJvdWdoIHRoZSBkaWZmZXJlbnQgcGxvdHMgdGhhdCBgZ2dyZXZlYWxgIGhhcyBtYWRlLiBOb3cgbGV0J3Mgc2F2ZSBhbGwgdGhlc2UgcGxvdHMgaW5kaXZpZHVhbGx5LgoKYGBge3J9CnJldmVhbF9zYXZlKAogIHBsb3RfbGlzdCwgIyBsaXN0IG9mIHBsb3RzCiAgImxpZmVfZXhwLnRpZmYiCikKYGBgCgojIyMgQWN0aXZpdGllcwoKIyMjIyBHcmVlbiAxCgpUaGVzZSBhY3Rpdml0aWVzIHdpbGwgdXNlIHRoZSBgZ2FwbWluZGVyYCBkYXRhc2V0IHdoaWNoIGlzIGEgc3Vic2V0IG9mIGNvdW50cnktYnktY291bnRyeSBHRFAgZnJvbSBnYXBtaW5kZXIub3JnIGZyb20gMjAxMCBhbmQgYW5vdGhlciBvZiBSJ3MgYnVpbHQtaW4gZGF0YXNldHMsIGBkaWFtb25kc2AsIHdoaWNoIGNvbnRhaW5zIGRhdGEgb24gZGlhbW9uZCBzaXplLCBjbGFyaXR5LCBhbmQgY29sb3IuCgpQZXJmb3JtIG5vcm1hbGl0eSB0ZXN0cyBvbiB0aGUgZ2FwbWluZGVyIGRhdGFzZXQuIExvb2sgc3BlY2lmaWNhbGx5IGF0IHRoZSBub3JtYWxpdHkgb2YgdGhlIGRhdGEgZnJvbSBFdXJvcGUuCgpgYGB7cn0KCmBgYAoKIyMjIyBHcmVlbiAyCgpDcmVhdGUgYSBoaXN0b2dyYW0gdXNpbmcgdGhlIGBkaWFtb25kc2AgZGF0YSBzZXQuIEZhY2V0IHNvIHRoYXQgdGhleSBhcmUgYWxpZ25lZCB2ZXJ0aWNhbGx5LgoKYGBge3J9CgpgYGAKCiMjIyMgQmx1ZSAxCgpDcmVhdGUgYSAyRCBiaW4gKGRlbnNpdHkpIHBsb3Qgc2hvd2luZyBHRFAgUGVyIENhcGl0YSBhbmQgTGlmZSBFeHBlY3RhbmN5IHVzaW5nIGBnZW9tX2JpbmQyZCgpYC4gVGhlbiwgY29tZSB1cCB3aXRoIGEgd2F5IHRvIHNob3cgYWxsIGZpdmUgY29udGluZW50cyBzZXBhcmF0ZWx5CgpgYGB7cn0KCmBgYAoKIyMjIyBCbHVlIDIKCk5vdyBhZGQgZGVuc2l0eSBsaW5lcyB0byB5b3VyIHBsb3QgdXNpbmcgYGdlb21fZGVuc2l0eTJkKClgLgoKYGBge3J9CgpgYGAKCiMjIyMgQmxhY2sgMQoKVXNpbmcgdGhlIGdhcG1pbmRlciBkYXRhLCB1c2Ugb25lIG9yIG1vcmUgdGVjaG5pcXVlcyB0byBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgTGlmZSBFeHBlY3RhbmN5IG92ZXIgdGltZSBmb3IgZWFjaCBjb250aW5lbnQuCgoqKkhpbnQ6IEV4cGxvcmUgdGhlIGdlb21zIHdlJ3ZlIHVzZWQgdG8gZGF5LCBvciB0cnkgYSBuZXcgb25lISoqIFlvdSBjYW4gbG9vayB1cCBhbGwgYXZhaWxhYmxlIGdlb21zIGhlcmU6IDxodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvaW5kZXguaHRtbD4KCmBgYHtyfQoKYGBgCgojIyMjIEJsYWNrIDIKClZpc3VhbGl6ZSBzcGVjaWZpYyBjb21wYXJpc29ucyBiZXR3ZWVuIGNvbnRpbmVudHMgYW5kIGxhYmVsIHdpdGggYnJhY2tldHMgYW5kIFAgdmFsdWVzIG9yIHNpZ25pZmljYW5jZSBtYXJrZXJzLgoKKipIaW50OiB1c2UgdGhlIHJlZmVyZW5jZSBhYm92ZSB0byB3b3JrIG91dCBob3cgdG8gZG8gdGhpcyoqCgpgYGB7cn0KCmBgYAo=